package com.googlecode.tidej.client.editor;

import java.util.ArrayList;

import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TextBox;
import com.googlecode.tidej.client.Dialog;
import com.googlecode.tidej.client.TidejController;
import com.googlecode.tidej.shared.model.Library;
import com.googlecode.tidej.shared.model.Operation;
import com.googlecode.tidej.shared.model.Parameter;
import com.googlecode.tidej.shared.model.Type;

public class OperationSignatureDialog extends Dialog {
  FlexTable table = new FlexTable();
  TextBox nameTextBox = new TextBox();
  Type type;
  Operation operation;
  Button okButton = new Button("Ok");
  TidejController controller;
  SuggestBox typeTextBox;
  static final int PARAMETER_ROW = 3;
  
  enum Mode {
    CREATE_TYPE, CREATE_OPERATION, EDIT_CONSTRUCTOR, EDIT_OPERATION;
  }
  Mode mode;
  
  public OperationSignatureDialog(TidejController controller) {
    super("");
    this.controller = controller;
    typeTextBox =  new SuggestBox(controller.getTypeSuggestOracle());
    getContentPanel().add(table);
    Button addButton = new Button("Add Parameter");
    addButton.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        insertRow(table.getRowCount()-1, null);
      }
    });
    table.setWidget(PARAMETER_ROW, 0, addButton);


    addButton(okButton);
    okButton.getElement().getStyle().setMarginLeft(4, Unit.PX);
    okButton.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        commit();
      }});
    
  }
  
  void insertRow(int index, Parameter parameter) {
    table.insertRow(index);
    TextBox nameTextBox = new TextBox();
    SuggestBox typeTextBox = new SuggestBox(controller.getTypeSuggestOracle());
    if (parameter != null) {
      nameTextBox.setText(parameter.getName());
      Type type = parameter.getType();
      if (type != null) {
        typeTextBox.setText(type.getName());
      }
    }
    table.setWidget(index, 0, nameTextBox);
    table.setWidget(index, 1, typeTextBox);
    Button addButton = new Button("\u00a0+\u00a0");
    addButton.addClickHandler(new AddClickHandler(addButton));
    table.setWidget(index, 2, addButton);
    Button removeButton = new Button("\u00a0\u2212\u00a0");
    removeButton.addClickHandler(new RemoveClickHandler(removeButton));
    table.setWidget(index, 3, removeButton);
  }
  
  void commit() {
    String name = nameTextBox.getText();
    if (type != null) {
      if(!Dialog.checkName("Name", name)) {
        return;
      }
    }
    ArrayList<Parameter> params = new ArrayList<Parameter>();
    for (int i = PARAMETER_ROW; i < table.getRowCount()-1; i++) {
      String pName = ((TextBox) table.getWidget(i, 0)).getText();
      if (!Dialog.checkName("Parameter Name " + i, pName)) {
        return;
      }
      for (Parameter p: params) {
        if (p.getName().equals(pName)) {
          Window.alert("Duplicate Parameter Name '" + pName + "'.");
        }
      }
      String ptName = ((SuggestBox) table.getWidget(i, 1)).getText();
      Type pType = controller.getModel().getType(ptName);
      if (pType == null) {
        Window.alert("Parameter Type '" + ptName + "' not found.");
        return;
      }
      params.add(new Parameter(pName, pType, 0));
    }

    switch(mode) {
    case CREATE_OPERATION:
      commitOperation(true, params);
      break;
    case CREATE_TYPE:
      commitType(true, params);
      break;
    case EDIT_OPERATION:
      commitOperation(false, params);
      break;
    case EDIT_CONSTRUCTOR:
      commitType(false, params);
      break;
    }
  }
  
  private void commitOperation(boolean create, ArrayList<Parameter> params) {
    String name = nameTextBox.getText();
    Operation existsForName = type.getOperation(name);
    if (existsForName != null && existsForName != operation) {
      Window.alert("Operation '" + name + "' exists already.");
      return;
    }
    if (type.getProperty(name) != null) {
      Window.alert("A property named '" + name + "' exists already.");
      return;
    }
    String retTypeName = typeTextBox.getText();
    if (retTypeName != null && !"void".equals(retTypeName) && !"".equals(retTypeName)) {
      Type retType = controller.getModel().getType(retTypeName);
      if (retType == null) {
        Window.alert("Parameter Type '" + retTypeName + "' not found.");
        return;
      }
      operation.setType(retType);
    }
    updateParameters(params);
    if (!create) {
      type.removeOperation(operation);
    }
    operation.setName(name);
    type.addOperation(operation);
    controller.saveCode(type);
    controller.refreshSubtree(type);
    controller.select(operation);
    hide();
  }
  
  private void commitType(boolean create, ArrayList<Parameter> params) {
    Library project = controller.getProject();
    String name = nameTextBox.getText();
    Type existingForName = controller.getModel().getType(name);
    if (existingForName != null && existingForName != type) {
      Window.alert("Type '" + name + "' exists already.");
      return;
    }
    updateParameters(params);
    if (!create) {
      project.deleteType(type);
    }
    type.setName(name);
    project.addType(type);
    controller.saveCode();
    controller.refreshSubtree(project);
    controller.select(type);
    hide();
  }
  
  private void updateParameters(ArrayList<Parameter> params) {
    operation.clearParmeters();
    for (Parameter p: params) {
      operation.addParameter(p);
    }
  }

  public void createType() {
    setText("Add New Type");
    type = new Type("");
    type.setConstructor(new Operation("", null));
    nameTextBox.setText(type.getName());
    mode = Mode.CREATE_TYPE;
    operation = type.getConstructor();
    run();
  } 
  
  public void createOperation(Type type) {
    setText("Add New Operation");
    this.type = type;
    mode = Mode.CREATE_OPERATION;
    operation = new Operation("", null);
    run();
  }
  

  public void editOperation(Operation operation) {
    this.operation = operation;
    this.type = operation.getOwner();
    nameTextBox.setText(operation.getName());
    if (operation == type.getConstructor()) {
      mode = Mode.EDIT_CONSTRUCTOR;
      setText("Edit Constructor");
    } else {
      mode = Mode.EDIT_OPERATION;
      setText("Edit Operation");
    }
    run();
  }

  
  
  private void run() {
    table.setWidget(1, 0, nameTextBox);
    table.setWidget(1, 1, typeTextBox);
    if (mode == Mode.CREATE_OPERATION || mode == Mode.EDIT_OPERATION) {
      table.setHTML(0, 0, "<b>Operation Name</b>");
      table.setHTML(0, 1, "<b>Return Type</b>");
    } else {
      table.setHTML(0, 0, "<b>Type Name</b>");
      typeTextBox.getElement().getStyle().setVisibility(Style.Visibility.HIDDEN);
    }
    
    table.setHTML(2, 0, "<b>Parameter Name</b>");
    table.setHTML(2, 1, "<b>Type</b>");
    for (Parameter p: operation.getParameters()) {
      insertRow(table.getRowCount()-1, p);
    }
    center();
    show();
  }

 
  class AddClickHandler implements ClickHandler {
    Button button;
    AddClickHandler(Button button) {
      this.button = button;
    }

    @Override
    public void onClick(ClickEvent event) {
      for (int i = PARAMETER_ROW; i < table.getRowCount()-1; i++) {
        if (table.getWidget(i, 2) == button) {
          insertRow(i, null);
          break;
        }
      }
    }
  }

  class RemoveClickHandler implements ClickHandler {
    Button button;
    RemoveClickHandler(Button button) {
      this.button = button;
    }

    @Override
    public void onClick(ClickEvent event) {
      for (int i = PARAMETER_ROW; i < table.getRowCount()-1; i++) {
        if (table.getWidget(i, 3) == button) {
          table.removeRow(i);
          break;
        }
      }
    }
  }

  
  
}
